Enable tmem functionality for PV on HVM guests. Guest kernel
authorKeir Fraser <keir.fraser@citrix.com>
Mon, 21 Jun 2010 18:19:25 +0000 (19:19 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Mon, 21 Jun 2010 18:19:25 +0000 (19:19 +0100)
must still be tmem-enabled to use this functionality (e.g.
won't work for Windows), but upstream Linux tmem (aka
cleancache and frontswap) patches apply cleanly on top
of PV on HVM patches.

Also, fix up some ASSERTS and code used only when bad guest
mfns are passed to tmem.  Previous code could crash Xen
if a buggy/malicious guest passes bad gmfns.

Signed-off-by: Dan Magenheimer <dan.magenheimer@oracle.com>
xen/arch/x86/hvm/hvm.c
xen/common/tmem.c
xen/common/tmem_xen.c
xen/include/xen/tmem_xen.h

index 1f1542bc43f7ade3a8e0198bd31f8c38620fada9..b803a30314b09a5f6e89798a503d073ed9f356c5 100644 (file)
@@ -2302,7 +2302,8 @@ static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = {
     HYPERCALL(event_channel_op),
     HYPERCALL(sched_op),
     HYPERCALL(set_timer_op),
-    HYPERCALL(hvm_op)
+    HYPERCALL(hvm_op),
+    HYPERCALL(tmem_op)
 };
 
 #else /* defined(__x86_64__) */
@@ -2355,7 +2356,8 @@ static hvm_hypercall_t *hvm_hypercall64_table[NR_hypercalls] = {
     HYPERCALL(event_channel_op),
     HYPERCALL(sched_op),
     HYPERCALL(set_timer_op),
-    HYPERCALL(hvm_op)
+    HYPERCALL(hvm_op),
+    HYPERCALL(tmem_op)
 };
 
 static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = {
@@ -2366,7 +2368,8 @@ static hvm_hypercall_t *hvm_hypercall32_table[NR_hypercalls] = {
     HYPERCALL(event_channel_op),
     HYPERCALL(sched_op),
     HYPERCALL(set_timer_op),
-    HYPERCALL(hvm_op)
+    HYPERCALL(hvm_op),
+    HYPERCALL(tmem_op)
 };
 
 #endif /* defined(__x86_64__) */
index ffc07669db59386d5b4e734a3eecae1bcfee647a..a3f236ae0bf50d3fe59f5727c695800a78c0c405 100644 (file)
@@ -1483,6 +1483,7 @@ copy_uncompressed:
         pgp_free_data(pgp, pool);
     if ( ( pgp->pfp = tmem_page_alloc(pool) ) == NULL )
         goto failed_dup;
+    pgp->size = 0;
     /* tmh_copy_from_client properly handles len==0 and offsets != 0 */
     ret = tmh_copy_from_client(pgp->pfp,cmfn,tmem_offset,pfn_offset,len,0);
     if ( ret == -EFAULT )
@@ -1492,7 +1493,6 @@ copy_uncompressed:
         if ( pcd_associate(pgp,NULL,0) == -ENOMEM )
             goto failed_dup;
     }
-    pgp->size = 0;
 
 done:
     /* successfully replaced data, clean up and return success */
@@ -1509,12 +1509,14 @@ done:
 bad_copy:
     /* this should only happen if the client passed a bad mfn */
     failed_copies++;
-ASSERT(0);
-    return -EFAULT;
+    ret = -EFAULT;
+    goto cleanup;
 
 failed_dup:
    /* couldn't change out the data, flush the old data and return
     * -ENOSPC instead of -ENOMEM to differentiate failed _dup_ put */
+    ret = -ENOSPC;
+cleanup:
     pgpfound = pgp_delete_from_obj(obj, pgp->index);
     ASSERT(pgpfound == pgp);
     pgp_delete(pgpfound,0);
@@ -1528,7 +1530,7 @@ failed_dup:
         tmem_spin_unlock(&obj->obj_spinlock);
     }
     pool->dup_puts_flushed++;
-    return -ENOSPC;
+    return ret;
 }
 
 
@@ -1579,6 +1581,7 @@ static NOINLINE int do_tmem_put(pool_t *pool,
         goto free;
     ASSERT(ret != -EEXIST);
     pgp->index = index;
+    pgp->size = 0;
 
     if ( len != 0 && client->compress )
     {
@@ -1615,7 +1618,6 @@ copy_uncompressed:
         if ( pcd_associate(pgp,NULL,0) == -ENOMEM )
             goto delete_and_free;
     }
-    pgp->size = 0;
 
 insert_page:
     if ( is_ephemeral(pool) )
@@ -1648,6 +1650,11 @@ insert_page:
         tot_good_eph_puts++;
     return 1;
 
+bad_copy:
+    /* this should only happen if the client passed a bad mfn */
+    ret = -EFAULT;
+    failed_copies++;
+
 delete_and_free:
     ASSERT((obj != NULL) && (pgp != NULL) && (pgp->index != -1));
     pgpdel = pgp_delete_from_obj(obj, pgp->index);
@@ -1669,12 +1676,6 @@ free:
     }
     pool->no_mem_puts++;
     return ret;
-
-bad_copy:
-    /* this should only happen if the client passed a bad mfn */
-    failed_copies++;
-ASSERT(0);
-    goto free;
 }
 
 static NOINLINE int do_tmem_get(pool_t *pool, uint64_t oid, uint32_t index,
@@ -1758,7 +1759,6 @@ static NOINLINE int do_tmem_get(pool_t *pool, uint64_t oid, uint32_t index,
 bad_copy:
     /* this should only happen if the client passed a bad mfn */
     failed_copies++;
-ASSERT(0);
     return -EFAULT;
 
 }
index 41c37bc57fcaca39d7196847815ce34848da8829..922403297ed609d059b68fef12c49a089cac4a9f 100644 (file)
@@ -101,7 +101,7 @@ static inline void *cli_mfn_to_va(tmem_cli_mfn_t cmfn, unsigned long *pcli_mfn)
     p2m_type_t t;
 
     cli_mfn = mfn_x(gfn_to_mfn(current->domain, cmfn, &t));
-    if (t != p2m_ram_rw)
+    if (t != p2m_ram_rw || cli_mfn == INVALID_MFN)
         return NULL;
     if (pcli_mfn != NULL)
         *pcli_mfn = cli_mfn;
index 84bb6dd238bac1a208abbea17c898554dce7d2ae..f409cbf1a9a7881f0700f88259d788bfb3959251 100644 (file)
@@ -456,7 +456,9 @@ typedef XEN_GUEST_HANDLE(tmem_op_t) tmem_cli_op_t;
 static inline int tmh_get_tmemop_from_client(tmem_op_t *op, tmem_cli_op_t uops)
 {
 #ifdef CONFIG_COMPAT
-    if ( is_pv_32on64_vcpu(current) )
+    if ( is_hvm_vcpu(current) ?
+         hvm_guest_x86_mode(current) != 8 :
+         is_pv_32on64_vcpu(current) )
     {
         int rc;
         enum XLAT_tmem_op_u u;